package org.thoughtworks.selenium.flex.driver;

import com.thoughtworks.selenium.CommandProcessor;
import com.thoughtworks.selenium.Selenium;
import org.thoughtworks.selenium.flex.ui.DisplayObject;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class SeleniumFlexDriver implements FlexDriver {

    private Selenium selenium;
    private CommandProcessor commandProcessor;

    private static final String COMMAND_RECORD_SECTION = "<Records>__STEPS__</Records>";
    private static final String COMMAND_STEP_SECTION = "<Step id=\"__ID__\" method=\"__METHOD_NAME__\"><Args value=\"__ARG_VALUES__\"/></Step>";

    public SeleniumFlexDriver(Selenium selenium) {
        try {
            this.selenium = selenium;
            this.commandProcessor = getCommandProcessor(selenium);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected SeleniumFlexDriver() {
    }

    private CommandProcessor getCommandProcessor(Selenium selenium)
            throws NoSuchFieldException, IllegalAccessException {
        Field field = selenium.getClass().getDeclaredField("commandProcessor");
        field.setAccessible(true);
        return (CommandProcessor) field.get(selenium);
    }

    public void execute(Command what, DisplayObject where) {
        execute(createCommandRecord(what, where));
    }

    public <T> T execute(Class<T> returnType, Command what, DisplayObject where) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String returnValue = executeValueOf(createCommandRecord(what, where));
        Method declaredMethod = returnType.getDeclaredMethod("valueOf", String.class);
        declaredMethod.setAccessible(true);
        return (T)declaredMethod.invoke(returnType, returnValue);
    }

    public String executeSeleniumAccessorCommand(String commandName, String... params) {
        return commandProcessor.getString(commandName, params);
    }

    public Boolean executeSeleniumAssertionCommand(String commandName, String... params) {
        return commandProcessor.getBoolean(commandName, params);
    }    

    public void executeSeleniumActionCommand(String commandName, String... params) {
        commandProcessor.doCommand(commandName, params);
    }    

    protected String execute(String commandXML) {
        System.out.println(commandXML);
        return commandProcessor.getString("getInvocationResponse", new String[]{commandXML,});
    }

    protected String executeValueOf(String commandXML) {
        System.out.println(commandXML);
        return commandProcessor.getString("getRequestedData", new String[]{commandXML,});
    }    

    protected String getElementsByFlexIdOrName(String id) {
        return commandProcessor.getString("getElementsByFlexId", new String[]{id,});
    }

    protected String createCommandRecord(Command command, DisplayObject where) {
        String step = createCommandStep(command, where);
        return COMMAND_RECORD_SECTION.replaceAll("__STEPS__", step);
    }

    protected String createCommandStep(Command command, DisplayObject where) {
        return createCommandStep(command, where.toString());
    }

    public String createCommandStep(Command command, String locator) {
        String commandStep = COMMAND_STEP_SECTION.replaceAll("__ID__", locator);
        commandStep = commandStep.replaceAll("__METHOD_NAME__", command.commandName);
        commandStep = commandStep.replaceAll("__ARG_VALUES__", command.commandArguments.join());
        return commandStep;
    }
}
